home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE19 / SERIAL / COMMDEV.PAS next >
Encoding:
Pascal/Delphi Source File  |  1997-01-26  |  26.9 KB  |  860 lines

  1. unit CommDev;
  2.  
  3. interface
  4.  
  5. uses WinProcs, WinTypes, Messages, SysUtils, Classes, Graphics, Forms;
  6.  
  7. type
  8.   TFlowControl = (fcNone,fcHardwareDSRDTR,fcHardwareCTSRTS,fcSoftware);
  9.  
  10.   TOnDataEvent = procedure(Buffer: Pointer; Length: Integer) of object;
  11.   TOnErrEvent = procedure of object;
  12.   TOnLineStatusChange = procedure(LineStatus: Boolean) of object;
  13.  
  14. type
  15.   TCommDevice = class
  16.     private
  17.       FBaudRate: Word;
  18.       FCtsTimeOut: Word;
  19.       FDeviceId: Integer;
  20.       FDataBits: Byte;
  21.       FDCB: TDCB;
  22.       FDoingDataReceive: Boolean;
  23.       FDoingDataTransmit: Boolean;
  24.       FDsrTimeOut: Word;
  25.       FEvents: Word;
  26.       FFlowControl: TFlowControl;
  27.       FInitString: string;
  28.       FNotifyWindow: HWnd;
  29.       FOnBreak: TOnErrEvent;
  30.       FOnCDChange: TOnLineStatusChange;
  31.       FOnCTSChange: TOnLineStatusChange;
  32.       FOnDSRChange: TOnLineStatusChange;
  33.       FOnData: TOnDataEvent;
  34.       FOnFrameErr: TOnErrEvent;
  35.       FOnOverrunErr: TOnErrEvent;
  36.       FOnParityErr: TOnErrEvent;
  37.       FParity: Byte;
  38.       FParityCheck: Boolean;
  39.       FParityDoReplaceChar: Boolean;
  40.       FParityReplacementChar: Char;
  41.       FReadBuffer: PChar; { }
  42.       FReceiveQueueSize: Integer;
  43.       FStopBits: Byte;
  44.       FTempOutputStoredBytes: Integer; { }
  45.       FTempOutBuffer: PChar; { }
  46.       FTransmitQueueSize: Integer;
  47.       FXFlowOffLimit: Word;
  48.       FXFlowOnLimit: Word;
  49.       FXoffChar: Char;
  50.       FXonChar: Char;
  51.       { Property access routines }
  52.       function  GetCDHigh: Boolean;
  53.       function  GetCTSHigh: Boolean;
  54.       function  GetDeviceId: Integer;
  55.       function  GetDSRHigh: Boolean;
  56.       function  GetInputByteCount: Integer;
  57.       function  GetOutpuByteCount: Integer;
  58.       function  GetDeviceOpen: Boolean;
  59.       function  GetRIHigh: Boolean;
  60.       procedure SetBaudRate(Value: Word);
  61.       procedure SetDataBits(Value: Byte);
  62.       procedure SetParity(Value: Byte);
  63.       procedure SetReceiveQueueSize(Value: Integer);
  64.       procedure SetStopBits(Value: Byte);
  65.       procedure SetTransmitQueueSize(Value: Integer);
  66.       { Other private routines }
  67.       procedure ConfigureDevice;
  68.       procedure InitialiseDevice;
  69.       procedure NotifyProcedure(var Message: TMessage);
  70.       procedure ProcessComError(Errors: Word);
  71.       procedure StoreRemainderInTempBuffer(Buff: PChar; BuffLen,BytesLeft: Integer);
  72.     protected
  73.       property  DeviceId: Integer read GetDeviceId;
  74.     public
  75.       { Constructor/destructor }
  76.       Constructor Create;
  77.       Destructor Destroy; override;
  78.       { Public methods }
  79.       procedure BreakTransmission;
  80.       procedure Close;
  81.       function  Dial(const Number: string): Boolean;
  82.       function  FlushInput: Boolean;
  83.       function  FlushOutput: Boolean;
  84.       procedure HangUp;
  85.       procedure Open(Port: Integer);
  86.       procedure ResumeTransmission;
  87.       function  Write(Buff: PChar; BuffLen: Integer): Boolean;
  88.       function  WriteLn(const S: string): Boolean;
  89.       { Properties }
  90.       property BaudRate: Word read FDCB.BaudRate write SetBaudRate;
  91.       property CDHigh: Boolean read GetCDHigh;
  92.       property CTSHigh: Boolean read GetCTSHigh;
  93.       property CtsTimeOut: Word read FCtsTimeout write FCtsTimeout;
  94.       property DataBits: Byte read FDCB.ByteSize write SetDataBits;
  95.       property DSRHigh: Boolean read GetDSRHigh;
  96.       property DsrTimeOut: Word read FDsrTimeout write FDsrTimeout;
  97.       property FlowControl: TFlowControl read FFlowControl write FFlowControl;
  98.       property InitString: string read FInitString write FinitString;
  99.       property InputByteCount: Integer read GetInputByteCount;
  100.       property OutputByteCount: Integer read GetOutpuByteCount;
  101.       property Parity: Byte read FDCB.Parity write SetParity;
  102.       property ParityCheck: Boolean read FParityCheck write FParityCheck;
  103.       property ParityDoReplaceChar: Boolean read FParityDoReplaceChar write FParityDoReplaceChar;
  104.       property ParityReplacementChar: Char read FParityReplacementChar write FParityReplacementChar;
  105.       property DeviceOpen: Boolean read GetDeviceOpen;
  106.       property ReceiveQueueSize: Integer read FReceiveQueueSize write SetReceiveQueueSize;
  107.       property RIHigh: Boolean read GetRIHigh;
  108.       property StopBits: Byte read FDCB.StopBits write SetStopBits;
  109.       property TransmitQueueSize: Integer read FTransmitQueueSize write SetTransmitQueueSize;
  110.       property XFlowOffLimit: Word read FXFlowOffLimit write FXFlowOffLimit;
  111.       property XFlowOnLimit: Word read FXFlowOnLimit write FXFlowOnLimit;
  112.       property XOffChar: Char read FXOffChar write FXOffChar;
  113.       property XOnChar: Char read FXOnChar write FXOnChar;
  114.       { Events }
  115.       property OnBreak: TOnErrEvent read FOnBreak write FOnBreak;
  116.       property OnCDChange: TOnLineStatusChange read FOnCDChange write FOnCDChange;
  117.       property OnCTSChange: TOnLineStatusChange read FOnCTSChange write FOnCTSChange;
  118.       property OnData: TOnDataEvent read FOnData write FOnData;
  119.       property OnDSRChange: TOnLineStatusChange read FOnDSRChange write FOnDSRChange;
  120.       property OnOverrunErr: TOnErrEvent read FOnOverrunErr write FOnOverrunErr;
  121.       property OnParityErr: TOnErrEvent read FOnParityErr write FOnParityErr;
  122.       property OnFrameErr: TOnErrEvent read FOnFrameErr write FOnFrameErr;
  123.   end;
  124.  
  125. implementation
  126.  
  127. const
  128.   ByteNotSet      = $FF;
  129.   DefaultInBuffer = 2048;
  130.   DefaultOutBuffer = 2048;
  131.   DefaultXflowTimeout = 300;
  132.   DeviceNotOpen = -1;
  133.   DialPrefix      = 'ATDT';
  134.   OutBufferSize = 4096;
  135.   ReadBufferSize  = 4096;
  136.   ReceiveTrigger  = -1;  { We use EV_RXCHAR instead }
  137.   TransmitTrigger = -1;  { We use EV_TXEMPTY to write backlog to the driver }
  138.  
  139. { From the article in the MSDN }
  140.  
  141. const
  142.   COMM_MSRShaddow = 35;
  143.   MSR_CTS         = $10;
  144.   MSR_DSR         = $20;
  145.   MSR_RI          = $40;
  146.   MSR_CD          = $80;
  147.  
  148. {==============================================================================}
  149. Constructor TCommDevice.Create;
  150. begin
  151.   FDeviceId := DeviceNotOpen;
  152.   FReceiveQueueSize := DefaultInBuffer;
  153.   FTransmitQueueSize := DefaultOutBuffer;
  154.   FParity := ByteNotSet;
  155.   FStopBits := ByteNotSet;
  156.   FCtsTimeOut := DefaultXflowTimeout;
  157.   FDsrTimeOut := DefaultXflowTimeout;
  158.   FXonChar := #17;
  159.   FXoffChar := #19;
  160.   FXFlowOnLimit := 32;
  161.   FXFlowOffLimit := 512;
  162.   FParityCheck := True;
  163.   FParityDoReplaceChar := True;
  164.   FParityReplacementChar := '*';
  165.   FInitString := 'ATZ';
  166.   FEvents := EV_RXCHAR or EV_TXEMPTY or EV_ERR or EV_BREAK or EV_CTS or EV_DSR or EV_RLSD;
  167.   FReadBuffer := MemAlloc(ReadBufferSize);
  168.   if FReadBuffer = nil then
  169.     Raise Exception.Create('MemAlloc failed creating internal buffer');
  170.   FTempOutBuffer := MemAlloc(OutBufferSize);
  171.   if FTempOutBuffer = nil then
  172.     Raise Exception.Create('MemAlloc failed creating temporary output buffer');
  173. end;
  174. {==============================================================================}
  175. Destructor TCommDevice.Destroy;
  176. begin
  177.   Close;
  178.   if FReadBuffer <> nil then
  179.     FreeMem(FReadBuffer,ReadBufferSize);
  180.   if FTempOutBuffer <> nil then
  181.     FreeMem(FTempOutBuffer,OutBufferSize);
  182.   inherited Destroy;
  183. end;
  184. {==============================================================================}
  185. { As SetCommBreak would overwrite any data currently being transmitted we keep }
  186. { checking the number of characters waiting to be transmitted to ensure that   }
  187. { is zero before we cause the device driver to drop the TX line. Subject to a  }
  188. { 5 second delay.                                                              }
  189. procedure TCommDevice.BreakTransmission;
  190. var
  191.   StartTicks: LongInt;
  192.  
  193. begin
  194.   if DeviceOpen then
  195.     begin
  196.       StartTicks := GetTickCount;
  197.       While OutputByteCount > 0 do
  198.         begin
  199.           Application.ProcessMessages;
  200.           if GetTickCount - StartTicks > 5000 then
  201.             Break;
  202.         end;
  203.       SetCommBreak(DeviceId);
  204.     end;
  205. end;
  206. {==============================================================================}
  207. procedure TCommDevice.Close;
  208. begin
  209.   if DeviceOpen then
  210.     begin
  211.       if FNotifyWindow <> 0 then
  212.     begin
  213.       EnableCommNotification(DeviceId,0,-1,-1);
  214.       DeallocateHWnd(FNotifyWindow);
  215.       FNotifyWindow := 0;
  216.     end;
  217.       if DeviceOpen then
  218.     CloseComm(DeviceId);
  219.       FDeviceId := DeviceNotOpen;
  220.       FillChar(FDCB,sizeof(TDCB),0);
  221.     end;
  222. end;
  223. {==============================================================================}
  224. { Called from the Open method to apply stored parameters to the commDevice     }
  225. procedure TCommDevice.ConfigureDevice;
  226. var
  227.   RC: Integer;
  228.  
  229. begin
  230.   RC := GetCommState(FDeviceId,FDCB);
  231.   If RC = 0 then
  232.     begin
  233.       if FBaudRate > 0 then
  234.     FDCB.BaudRate := FBaudRate;
  235.       if FDataBits > 0 then
  236.     FDCB.ByteSize := FDataBits;
  237.       if FParity <> ByteNotSet then
  238.     FDCB.Parity := FParity;
  239.       if FStopBits <> ByteNotSet then
  240.     FDCB.StopBits := FStopBits;
  241.  
  242.       With FDCB do
  243.     begin
  244.       { Set flow control }
  245.  
  246.       Flags := dcb_Binary;
  247.       XOnLim := 0;
  248.       XOffLim := 0;
  249.       CtsTimeOut := 0;
  250.       DsrTimeOut := 0;
  251.       XOnChar := #0;
  252.       XOffChar := #0;
  253.  
  254.       case FFlowControl of
  255.  
  256.         fcHardwareDSRDTR:
  257.           begin
  258.         Flags := Flags or dcb_OutxDsrFlow or dcb_Dtrflow;
  259.         XOnLim := XFlowOnLimit;
  260.         XOffLim := XFlowOffLimit;
  261.         DsrTimeOut := FdsrTimeout;
  262.           end;
  263.  
  264.         fcHardwareCTSRTS:
  265.           begin
  266.         Flags := Flags or dcb_OutxCtsFlow or dcb_Rtsflow;
  267.         XOnLim := XFlowOnLimit;
  268.         XOffLim := XFlowOffLimit;
  269.         CtsTimeOut := FCtsTimeout;
  270.           end;
  271.  
  272.         fcSoftware:
  273.           begin
  274.         Flags := Flags or dcb_InX or dcb_OutX;
  275.         XOnLim := XFlowOnLimit;
  276.         XOffLim := XFlowOffLimit;
  277.         XOnChar := FXOnChar;
  278.         XOffChar := FXOnChar;
  279.           end;
  280.  
  281.       end;
  282.  
  283.       { Set parity checking options }
  284.  
  285.       if ParityCheck then
  286.         Flags := flags or dcb_Parity;
  287.  
  288.       if ParityDoReplaceChar then
  289.         begin
  290.           Flags := flags or dcb_PeChar;
  291.           FDCB.PeChar := ParityReplacementChar;
  292.         end;
  293.  
  294.     end; { With FDCB do }
  295.  
  296.       RC := SetCommstate(FDCB);
  297.  
  298.       if RC = 0 then
  299.     begin
  300.       { Set up notification events }
  301.       FNotifyWindow := AllocateHWnd(NotifyProcedure);
  302.       SetCommEventMask(DeviceId,FEvents);
  303.       EnableCommNotification(DeviceId,FNotifyWindow,ReceiveTrigger,TransmitTrigger);
  304.     end
  305.       else
  306.     Raise Exception.CreateFmt('Failed to configure Device. SetCommstate ended with error %d.',[RC]);
  307.     end
  308.   else
  309.     Raise Exception.CreateFmt('GetCommState ended with %d when trying to configure Device.',[RC]);
  310. end;
  311. {==============================================================================}
  312. procedure TCommDevice.InitialiseDevice;
  313. begin
  314.   { Assert the DTR line }
  315.   EscapeCommFunction(FDeviceId,SETDTR);
  316.   FTempOutputStoredBytes := 0;
  317.   if InitString <> '' then
  318.     WriteLn(InitString);
  319. end;
  320. {==============================================================================}
  321. function TCommDevice.Dial(const Number: string): Boolean;
  322. begin
  323.   Result := DeviceOpen;
  324.   if DeviceOpen then
  325.     WriteLn(DialPrefix + Number);
  326. end;
  327. {==============================================================================}
  328. function TCommDevice.FlushInput: Boolean;
  329. begin
  330.   if DeviceOpen then
  331.     Result := FlushComm(DeviceId,1) = 0
  332.   else
  333.     Result := False;
  334. end;
  335. {==============================================================================}
  336. function TCommDevice.FlushOutput: Boolean;
  337. begin
  338.   if DeviceOpen then
  339.     Result := FlushComm(DeviceId,0) = 0
  340.   else
  341.     Result := False;
  342. end;
  343. {==============================================================================}
  344. function TCommDevice.GetCDHigh: Boolean;
  345. begin
  346.   if DeviceOpen then
  347.     Result := (PByteArray(SetCommEventMask(DeviceId,FEvents))^[COMM_MSRShaddow] and MSR_CD) = MSR_CD
  348.   else
  349.     Result := False;
  350. end;
  351. {==============================================================================}
  352. function TCommDevice.GetCTSHigh: Boolean;
  353. begin
  354.   if DeviceOpen then
  355.     Result := (PByteArray(SetCommEventMask(DeviceId,FEvents))^[COMM_MSRShaddow] and MSR_CTS) = MSR_CTS;
  356. end;
  357. {==============================================================================}
  358. function TCommDevice.GetDeviceId: Integer;
  359. begin
  360.   Result := FDeviceId;
  361. end;
  362. {==============================================================================}
  363. function TCommDevice.GetDSRHigh: Boolean;
  364. begin
  365.   if DeviceOpen then
  366.     Result := (PByteArray(SetCommEventMask(DeviceId,FEvents))^[COMM_MSRShaddow] and MSR_DSR) = MSR_DSR
  367.   else
  368.     Result := False;
  369. end;
  370. {==============================================================================}
  371. function TCommDevice.GetInputByteCount: Integer;
  372. var
  373.   CommStat: TComStat;
  374.  
  375. begin
  376.   if DeviceOpen then
  377.     begin
  378.       GetCommError(DeviceId,CommStat);
  379.       Result := CommStat.cbInQue;
  380.     end
  381.   else
  382.     Result := 0;
  383. end;
  384. {==============================================================================}
  385. function TCommDevice.GetOutpuByteCount: Integer;
  386. var
  387.   ComStat: TComStat;
  388.  
  389. begin
  390.   if DeviceOpen then
  391.     begin
  392.       GetCommError(DeviceId,ComStat);
  393.       Result := ComStat.cbOutQue;
  394.     end
  395.   else
  396.     Result := 0;
  397. end;
  398. {==============================================================================}
  399. function TCommDevice.GetDeviceOpen: Boolean;
  400. begin
  401.   Result := DeviceId <> DeviceNotOpen;
  402. end;
  403. {==============================================================================}
  404. function TCommDevice.GetRIHigh: Boolean;
  405. begin
  406.   if DeviceOpen then
  407.     Result := (PByteArray(SetCommEventMask(DeviceId,FEvents))^[COMM_MSRShaddow] and MSR_RI) = MSR_RI
  408.   else
  409.     Result := False;
  410. end;
  411. {==============================================================================}
  412. procedure TCommDevice.HangUp;
  413. var
  414.   StartTicks: LongInt;
  415.  
  416. begin
  417.   if DeviceOpen then
  418.     begin
  419.       EscapeCommFunction(DeviceId,CLRDTR);
  420.       StartTicks := GetTickCount;
  421.       While GetTickCount - StartTicks < 500 do
  422.     Application.ProcessMessages;
  423.       EscapeCommFunction(DeviceId,SETDTR);
  424.     end;
  425. end;
  426. {==============================================================================}
  427. procedure TCommDevice.NotifyProcedure(var Message: TMessage);
  428. var
  429.   EventFlags: Word;
  430.   LastError: Word;
  431.   ComStat: TComStat;
  432.   BytesRead: Integer;
  433.   BytesWritten: Integer;
  434.   Errors: Integer;
  435.  
  436. begin
  437.   With Message do
  438.     if (Msg = WM_COMMNOTIFY) and (wParam = DeviceId) then
  439.       begin
  440.     { Although we pass -1 to EnableCommnotification this has been added to }
  441.     { show how you could process this trigger.                             }
  442.     if LoWord(LParam) and CN_RECEIVE = CN_RECEIVE then
  443.       begin
  444.         { This flag is to ensure that this code is not executed the if     }
  445.         { another message is received while we are reading the data.       }
  446.         if not FDoingDataReceive then
  447.           begin
  448.         FDoingDataReceive := True;
  449.         try
  450.           BytesRead := ReadComm(DeviceId,FReadBuffer,ReadBufferSize);
  451.           while BytesRead > 0 do
  452.             begin
  453.               if Assigned(FOnData) then
  454.             FOnData(FReadBuffer,BytesRead);
  455.               Application.ProcessMessages;
  456.               BytesRead := ReadComm(DeviceId,FReadBuffer,ReadBufferSize);
  457.             end;
  458.           if BytesRead <=0 then
  459.             begin
  460.                       Errors := GetCommError(DeviceId,ComStat);
  461.                       if Errors <> 0 then
  462.                         ProcessComError(Errors);
  463.             end;
  464.         finally
  465.           FDoingDataReceive := False;
  466.         end;
  467.           end;
  468.       end;
  469.     { Although we pass -1 to EnableCommnotification this has been added to }
  470.     { show how you could process this trigger.                             }
  471.     if LoWord(LParam) and CN_TRANSMIT = CN_TRANSMIT then
  472.       begin
  473.         { This flag is to ensure that this code is not executed the if     }
  474.         { another message is received while we are writing the data.       }
  475.         if not FDoingDataTransmit then
  476.           begin
  477.         FDoingDataTransmit := True;
  478.         try
  479.           if FTempOutputStoredBytes <> 0 then
  480.             begin
  481.               BytesWritten := WriteComm(DeviceId,FTempOutBuffer,FTempOutputStoredBytes);
  482.               if BytesWritten < 0 then
  483.             begin
  484.               Errors := GetCommError(DeviceId,ComStat);
  485.               ProcessComError(Errors);
  486.               BytesWritten := -BytesWritten;
  487.               StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,
  488.                 FTempOutputStoredBytes - BytesWritten);
  489.             end
  490.               else
  491.             if BytesWritten = 0 then
  492.               begin
  493.                    Errors := GetCommError(DeviceId,ComStat);
  494.                 if Errors <> 0 then
  495.                               ProcessComError(Errors);
  496.                             StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,FTempOutputStoredBytes);
  497.               end
  498.             else
  499.               if BytesWritten < FTempOutputStoredBytes then
  500.                 begin
  501.                   StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,
  502.                 FTempOutputStoredBytes - BytesWritten);
  503.                 end
  504.               else
  505.                 FTempOutputStoredBytes := 0;
  506.             end
  507.         finally
  508.           FDoingDataTransmit := False;
  509.         end;
  510.           end;
  511.       end;
  512.  
  513.     if LoWord(LParam) and CN_EVENT = CN_EVENT then
  514.       begin
  515.         EventFlags := GetCommEventMask(DeviceId,FEvents);
  516.         LastError := GetCommError(DeviceId,ComStat);
  517.  
  518.         { Process data if we have received it }
  519.  
  520.         if EventFlags and EV_RXCHAR = EV_RXCHAR then
  521.           begin
  522.         if not FDoingDataReceive then
  523.           begin
  524.             FDoingDataReceive := True;
  525.             try
  526.               BytesRead := ReadComm(DeviceId,FReadBuffer,ReadBufferSize);
  527.               while BytesRead > 0 do
  528.             begin
  529.               if Assigned(FOnData) then
  530.                 FOnData(FReadBuffer,BytesRead);
  531.               Application.ProcessMessages;
  532.               BytesRead := ReadComm(DeviceId,FReadBuffer,ReadBufferSize);
  533.             end;
  534.                if BytesRead <=0 then
  535.                 begin
  536.                           Errors := GetCommError(DeviceId,ComStat);
  537.                           if Errors <> 0 then
  538.                             ProcessComError(Errors);
  539.                  end;
  540.             finally
  541.               FDoingDataReceive := False;
  542.             end;
  543.           end;
  544.           end;
  545.  
  546.         if EventFlags and EV_TXEMPTY = EV_TXEMPTY then
  547.           begin
  548.         { This flag is to ensure that this code is not executed the if }
  549.         { another message is received while we are transmitting the    }
  550.         { temp data buffer.                                            }
  551.         if not FDoingDataTransmit then
  552.           begin
  553.             FDoingDataTransmit := True;
  554.             try
  555.               if FTempOutputStoredBytes <> 0 then
  556.             begin
  557.               BytesWritten := WriteComm(DeviceId,FTempOutBuffer,FTempOutputStoredBytes);
  558.               if BytesWritten < 0 then
  559.                 begin
  560.                   Errors := GetCommError(DeviceId,ComStat);
  561.                   ProcessComError(Errors);
  562.                   BytesWritten := -BytesWritten;
  563.                   StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,
  564.                 FTempOutputStoredBytes - BytesWritten);
  565.                 end
  566.               else
  567.                 if BytesWritten = 0 then
  568.                   begin
  569.                                 Errors := GetCommError(DeviceId,ComStat);
  570.                                 if Errors <> 0 then
  571.                                   ProcessComError(Errors);
  572.                 StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,FTempOutputStoredBytes);
  573.                   end
  574.                 else
  575.                   if BytesWritten < FTempOutputStoredBytes then
  576.                 begin
  577.                   StoreRemainderInTempBuffer(FTempOutBuffer,FTempOutputStoredBytes,
  578.                     FTempOutputStoredBytes - BytesWritten);
  579.                 end
  580.                   else
  581.                 FTempOutputStoredBytes := 0;
  582.                 end
  583.             finally
  584.               FDoingDataTransmit := False;
  585.             end;
  586.           end;
  587.           end;
  588.  
  589.         if EventFlags and EV_ERR = EV_ERR then
  590.           begin
  591.         { A hardware generated error }
  592.                 ProcessComError(LastError);
  593.           end;
  594.  
  595.         if EventFlags and EV_CTS = EV_CTS then
  596.           if Assigned(FOnCTSChange) then
  597.         FOnCTSChange(CTSHigh);
  598.  
  599.         if EventFlags and EV_DSR = EV_DSR then
  600.           if Assigned(FOnDSRChange) then
  601.         FOnDSRChange(DSRHigh);
  602.  
  603.         if EventFlags and EV_RLSD = EV_RLSD then
  604.           if Assigned(FOnCDChange) then
  605.         FOnCDChange(CDHigh);
  606.  
  607.         if EventFlags and EV_BREAK = EV_BREAK then
  608.           if Assigned(FOnBreak) then
  609.         FOnBreak;
  610.       end;
  611.       end;
  612. end;
  613. {==============================================================================}
  614. procedure TCommDevice.Open(Port: Integer);
  615. var
  616.   CommDeviceName: array[0..4] of char;
  617.   TmpID: Integer;
  618.  
  619. begin
  620.   if not DeviceOpen then
  621.     begin
  622.       TmpID := OpenComm(StrPCopy(CommDeviceName,Format('COM%D',[Port])),ReceiveQueueSize,TransmitQueueSize);
  623.       try
  624.     case TmpID of
  625.       0..32767:
  626.         begin
  627.           FDeviceID := TmpID;
  628.           ConfigureDevice;
  629.           InitialiseDevice;
  630.         end;
  631.       IE_BADID:    Raise Exception.Create('The device identifier is invalid or unsupported.');
  632.       IE_BAUDRATE: Raise Exception.Create('The device''s baud rate is unsupported.');
  633.       IE_BYTESIZE: Raise Exception.Create('The specified byte size is invalid.');
  634.       IE_DEFAULT:  Raise Exception.Create('The default parameters are in error.');
  635.       IE_HARDWARE: Raise Exception.Create('The hardware is not available (is locked by another device).');
  636.       IE_MEMORY:   Raise Exception.Create('The function cannot allocate the queues.');
  637.       IE_NOPEN:    Raise Exception.Create('The device is not open.');
  638.       IE_OPEN:     Raise Exception.Create('The device is already open.');
  639.     else
  640.       Raise Exception.CreateFmt('OpenComm failed with error %d.',[FDeviceId]);
  641.     end;
  642.       except
  643.     on Exception do
  644.       begin
  645.         CloseComm(FDeviceId);
  646.         FDeviceId := DeviceNotOpen;
  647.         Raise;
  648.       end;
  649.       end;
  650.  
  651.     end
  652.   else
  653.     Raise Exception.Create('Device is already open');
  654. end;
  655. {==============================================================================}
  656. { See the on-line help for GetCommError for other errors which could be trapped}
  657. procedure TCommDevice.ProcessComError(Errors: Word);
  658. begin
  659.   if Errors and CE_RXPARITY = CE_RXPARITY then
  660.     if Assigned(FOnParityErr) then
  661.       FOnParityErr;
  662.   if Errors and CE_OVERRUN = CE_OVERRUN then
  663.     if Assigned(FOnOverrunErr) then
  664.       FOnOverrunErr;
  665.   if Errors and CE_FRAME = CE_FRAME then
  666.     if Assigned(FOnFrameErr) then
  667.       FOnFrameErr;
  668. end;
  669. {==============================================================================}
  670. procedure TCommDevice.ResumeTransmission;
  671. begin
  672.   ClearCommBreak(DeviceId);
  673. end;
  674. {==============================================================================}
  675. procedure TCommDevice.SetBaudRate(Value: Word);
  676. var
  677.   OldBaudRate: Word;
  678.   RC: Integer;
  679.  
  680. begin
  681.  FBaudRate := Value;
  682.  if DeviceOpen then
  683.    begin
  684.      OldBaudRate := FDCB.BaudRate;
  685.      FDCB.BaudRate := Value;
  686.      RC := SetCommstate(FDCB);
  687.      if RC <> 0 then
  688.        begin
  689.      FDCB.BaudRate := OldBaudRate;
  690.      Raise Exception.CreateFmt('Failed to change Device baud rate. SetCommstate ended with error %d.',[RC]);
  691.        end;
  692.    end;
  693. end;
  694. {==============================================================================}
  695. procedure TCommDevice.SetDataBits(Value: Byte);
  696. var
  697.   OldByteSize: Byte;
  698.   RC: Integer;
  699.  
  700. begin
  701.   if Value in [5..8] then
  702.     begin
  703.       FDataBits := Value;
  704.       if DeviceOpen then
  705.     begin
  706.       OldByteSize := FDCB.ByteSize;
  707.       FDCB.ByteSize := Value;
  708.       RC := SetCommstate(FDCB);
  709.       if RC <> 0 then
  710.         begin
  711.           FDCB.ByteSize := OldByteSize;
  712.           Raise Exception.CreateFmt('Failed to change Device data size. SetCommstate ended with error %d.',[RC]);
  713.         end;
  714.     end
  715.     end;
  716. end;
  717. {==============================================================================}
  718. procedure TCommDevice.SetParity(Value: Byte);
  719. var
  720.   OldParity: Byte;
  721.   RC: Integer;
  722.  
  723. begin
  724.   if Value in [NOPARITY,ODDPARITY,EVENPARITY,MARKPARITY,SPACEPARITY] then
  725.     begin
  726.       FParity := Value;
  727.       if DeviceOpen then
  728.     begin
  729.       OldParity := FDCB.Parity;
  730.       FDCB.Parity := Value;
  731.       RC := SetCommstate(FDCB);
  732.       if RC <> 0 then
  733.         begin
  734.           FDCB.Parity := OldParity;
  735.           Raise Exception.CreateFmt('Failed to change Device parity option. SetCommstate ended with error %d.',[RC]);
  736.         end;
  737.     end
  738.     end;
  739. end;
  740. {==============================================================================}
  741. procedure TCommDevice.SetReceiveQueueSize(Value: Integer);
  742. begin
  743.   if Value > 0 then
  744.     FReceiveQueueSize := Value;
  745. end;
  746. {==============================================================================}
  747. procedure TCommDevice.SetStopBits(Value: Byte);
  748. var
  749.   OldStopBits: Byte;
  750.   RC: Integer;
  751.  
  752. begin
  753.   If Value in [ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS] then
  754.     begin
  755.       FStopBits := Value;
  756.       if DeviceOpen then
  757.     begin
  758.       OldStopBits := FDCB.StopBits;
  759.       FDCB.StopBits := Value;
  760.       RC := SetCommstate(FDCB);
  761.       if RC <> 0 then
  762.         begin
  763.           FDCB.Parity := OldStopBits;
  764.           Raise Exception.CreateFmt('Failed to change Device stop bits option. SetCommstate ended with error %d.',[RC]);
  765.         end;
  766.     end;
  767.     end;
  768. end;
  769. {==============================================================================}
  770. procedure TCommDevice.SetTransmitQueueSize(Value: Integer);
  771. begin
  772.   if Value > 0 then
  773.     FTransmitQueueSize := Value;
  774. end;
  775. {==============================================================================}
  776. procedure TCommDevice.StoreRemainderInTempBuffer(Buff: PChar; BuffLen,BytesLeft: Integer);
  777. begin
  778.   FTempOutputStoredBytes := BytesLeft;
  779.   Move(Buff[BuffLen-BytesLeft],FTempOutBuffer^,BytesLeft);
  780. end;
  781. {==============================================================================}
  782. { This function will either write the whole block or nothing                   }
  783. function TCommDevice.Write(Buff: PChar; BuffLen: Integer): Boolean;
  784. var
  785.   BytesWritten: Integer;
  786.   Errors: Integer;
  787.   Comstat: TComStat;
  788.   StartTicks: LongInt;
  789.  
  790. begin
  791.   if DeviceOpen then
  792.     begin
  793.       { If we are already waiting to send the last block, caller will have to  }
  794.       { call us again after receiving false.                                   }
  795.       Result := False;
  796.       StartTicks := GetTickCount;
  797.       While FTempOutputStoredBytes <> 0 do
  798.     begin
  799.       Application.ProcessMessages;
  800.       if GetTickCount - StartTicks > 5000 then
  801.         Break;
  802.     end;
  803.       if FTempOutputStoredBytes = 0 then
  804.     begin
  805.       BytesWritten := WriteComm(DeviceId,Buff,BuffLen);
  806.       if BytesWritten < 0 then
  807.         begin
  808.           Errors := GetCommError(DeviceId,ComStat);
  809.           ProcessComError(Errors);
  810.           BytesWritten := -BytesWritten;
  811.           StoreRemainderInTempBuffer(Buff,BuffLen,BuffLen - BytesWritten);
  812.         end
  813.       else
  814.         if BytesWritten = 0 then
  815.           begin
  816.                Errors := GetCommError(DeviceId,ComStat);
  817.                 if Errors <> 0 then
  818.                  ProcessComError(Errors);
  819.         StoreRemainderInTempBuffer(Buff,BuffLen,BuffLen);
  820.           end
  821.         else
  822.           if BytesWritten < BuffLen then
  823.         begin
  824.           StoreRemainderInTempBuffer(Buff,BuffLen,BuffLen - BytesWritten);
  825.         end;
  826.       Result := True;
  827.     end;
  828.     end
  829.   else
  830.     Raise Exception.Create('Comm Device is not open.');
  831. end;
  832. {==============================================================================}
  833. function TCommDevice.WriteLn(const S: string): Boolean;
  834. var
  835.   Wrk: array[0..255] of char;
  836.   StartTicks: LongInt;
  837.  
  838.   I: Integer;
  839.  
  840. begin
  841.   Result := DeviceOpen;
  842.   if Result then
  843.     begin
  844.       StrPCopy(Wrk,s);
  845.       Wrk[Length(S)] := #13;
  846.       StartTicks := GetTickCount;
  847.       While not Write(Wrk,Length(S)+1) do
  848.     begin
  849.       Application.ProcessMessages;
  850.       if GetTickCount - StartTicks > 5000 then
  851.         begin
  852.           Result := False;
  853.           Break;
  854.         end;
  855.     end;
  856.     end;
  857. end;
  858.  
  859. end.
  860.